/**
 * @Author: dn-jinmin
 * @File:  main
 * @Version: 1.0.0
 * @Date: 2023/4/23
 * @Description:
 */

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"sync"
	"time"
)

// 业务场景：在web项目或者教学系统中业务人员需要给指定的用户群发短信或者邮件通知消息。

type User struct {
	id    int
	name  string
	phone string
	email string
}

// 生成用户
// 在生产环境下应是从数据库或excel中获取
func generateUser(n int) []*User {
	users := make([]*User, n)
	for i := 0; i < n; i++ {
		users[i] = &User{
			id:    i + 1,
			name:  fmt.Sprintf("User_%d", i+1),
			phone: fmt.Sprintf("138%08d", i),
			email: fmt.Sprintf("user%d@example.com", i+1),
		}
	}
	return users
}

type Response struct {
	Code int
	Msg  string
}

func Post(api string, body map[string]interface{}) (*Response, error) {

	param, err := json.Marshal(body)
	if err != nil {
		return nil, err
	}

	// 请求
	req, err := http.NewRequest("post", api, bytes.NewBuffer(param))
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	// 处理返回结果
	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	var res Response
	if err := json.Unmarshal(data[:], &res); err != nil {
		return nil, err
	}

	return &res, nil
}

const Success = 0

// 定义发送短信
func sendSMS(phone string, content string) {
	// 发送短信的代码
	fmt.Printf("正在给%s发送短信...\n", phone)

	var api = "http://127.0.0.1:8080/send_sms"
	param := map[string]interface{}{
		"phone":   phone,
		"content": content,
	}

	res, err := Post(api, param)
	if err != nil {
		fmt.Printf("%s短信发送失败: %v\n", phone, err)
		return
	}
	if res.Code != Success {
		fmt.Printf("%s短信发送失败: %s\n", phone, res.Msg)
		return
	}

	fmt.Printf("%s短信发送成功\n", phone)
}

func main() {
	// 获取需要发送短信的用户
	users := generateUser(10000)

	// 设置缓冲通道，容量为10，限制每秒钟最多发送10条短信
	rateLimit := make(chan struct{}, 1000)
	for i := 0; i < 1000; i++ {
		rateLimit <- struct{}{}
	}

	// 对用户发送短信
	var wg sync.WaitGroup
	for _, user := range users {
		wg.Add(1)
		go func() {
			defer wg.Done()

			// 从缓冲通道中取出一个元素，表示可以发送短信
			<-rateLimit

			sendSMS(user.phone, "客户你好..")

			// 将一个元素放回缓冲通道中，表示短信发送结束
			time.Sleep(time.Second)
			rateLimit <- struct{}{}
		}()
	}

	// 等待协程结束
	wg.Wait()
}
